/**
 * @file cdnet_core.h
 * @brief CDNET核心协议头文件 - 命名空间和套接字管理
 * @details
 * CDNET协议栈的核心头文件，定义了命名空间(namespace)、套接字(socket)、
 * 网络接口等关键数据结构，以及路由、发送、接收等核心API接口。
 * 
 * 功能特性：
 * - 命名空间管理：支持多网络环境
 * - 套接字系统：类似BSD套接字的网络编程接口
 * - 内存池管理：高效的数据包和帧内存分配
 * - 路由系统：智能的网络路由和转发
 * - 多接口支持：可同时管理多个网络接口
 * 
 * 架构说明：
 * - cdn_ns_t: 命名空间，管理整个网络环境
 * - cdn_sock_t: 套接字，提供网络通信端点
 * - cdn_intf_t: 网络接口，表示物理或虚拟网络连接
 * - cdn_pkt_t: 数据包，网络传输的基本单元
 * 
 * @author CDBUS开发团队
 * @copyright MIT License
 * @date 2024
 * @note 基于Duke Fong的cdnet core实现
 * 
 * @warning 需要配合cd_list.h和cdnet.h使用
 * 
 * 使用示例：
 * @code
 * cdn_ns_t ns;
 * list_head_t free_pkt, free_frm;
 * cdn_pkt_t pkt_pool[8];
 * cd_frame_t frame_pool[8];
 * 
 * // 初始化命名空间
 * cdn_init_ns(&ns, &free_pkt, &free_frm);
 * 
 * // 添加网络接口
 * cdn_add_intf(&ns, &my_dev, 0x01, 0x10);
 * 
 * // 创建并绑定套接字
 * cdn_sock_t sock;
 * sock.ns = &ns;
 * sock.port = 0x1234;
 * cdn_sock_bind(&sock);
 * @endcode
 */

#ifndef CDNET_CORE_H
#define CDNET_CORE_H

#include <Arduino.h>
#include "cd_list.h"
#include "cdnet.h"

#ifndef CDN_INTF_MAX
/**
 * @brief 最大网络接口数量
 * @details 定义单个命名空间支持的最大网络接口数量
 * @note 可根据实际需求调整此值
 */
#define CDN_INTF_MAX            1
#endif

/**
 * @struct cdn_sock_t
 * @brief CDNET套接字结构体
 * @details 提供类似BSD套接字的网络通信端点，支持绑定端口、发送接收数据
 */
typedef struct {
    list_node_t     node;       /**< 链表节点，用于套接字管理 */
    struct _cdn_ns  *ns;        /**< 所属命名空间指针 */
    uint16_t        port;       /**< 绑定的端口号 */
    list_head_t     rx_head;    /**< 接收数据包链表头 */
    bool            tx_only;    /**< 是否仅发送模式 */
} cdn_sock_t;

/**
 * @struct cdn_intf_t
 * @brief 网络接口结构体
 * @details 表示一个网络接口，可以是CDBUS设备或CDCTL设备
 */
typedef struct {
    void           *dev;        /**< 底层设备指针(CDBUS或CDCTL) */
    
    // 接口地址信息
    uint8_t         net;        /**< 网络号 */
    uint8_t         mac;        /**< MAC地址 */
} cdn_intf_t;

/**
 * @struct _cdn_ns
 * @brief CDNET命名空间结构体
 * @details 管理整个CDNET网络环境，包括套接字、接口、内存池等
 */
typedef struct _cdn_ns {
    list_head_t     *free_pkt;  /**< 空闲数据包内存池 */
    list_head_t     *free_frm;  /**< 空闲帧内存池 */
    list_head_t     socks;      /**< 已绑定套接字链表 */
    cdn_intf_t      intfs[CDN_INTF_MAX]; /**< 网络接口数组 */
    cdn_pkt_t       *rx_tmp;    /**< 临时接收数据包 */
} cdn_ns_t; // 命名空间

/**
 * @defgroup cdnet_core_routing 路由系统API
 * @brief 网络路由和数据包转发功能
 * @{
 */

/**
 * @brief 路由查找函数
 * @param ns 命名空间指针
 * @param pkt 数据包指针
 * @return 目标网络接口指针，NULL表示无可用路由
 * 
 * @note 根据数据包的目标地址查找最佳路由
 * @warning 需要在发送前调用此函数
 */
cdn_intf_t *cdn_route(cdn_ns_t *ns, cdn_pkt_t *pkt);

/**
 * @brief 发送帧数据
 * @param ns 命名空间指针
 * @param pkt 数据包指针
 * @return 发送结果：0成功，负值表示错误
 * 
 * @note 通过指定的网络接口发送完整帧
 * @warning 需要先调用cdn_route()确定路由
 */
int cdn_send_frame(cdn_ns_t *ns, cdn_pkt_t *pkt);

/**
 * @brief 发送数据包
 * @param ns 命名空间指针
 * @param pkt 数据包指针
 * @return 发送结果：0成功，负值表示错误
 * 
 * @note 高层发送接口，自动处理路由和协议头
 * @warning 数据包发送后会被释放
 */
int cdn_send_pkt(cdn_ns_t *ns, cdn_pkt_t *pkt);

/** @} */ // 路由系统API

/**
 * @defgroup cdnet_core_socket 套接字管理API
 * @brief 套接字绑定、发送、接收功能
 * @{
 */

/**
 * @brief 绑定套接字到端口
 * @param sock 套接字指针
 * @return 绑定结果：0成功，负值表示错误
 * 
 * @note 绑定后套接字可接收该端口的数据
 * @warning 端口必须唯一，不能重复绑定
 */
int cdn_sock_bind(cdn_sock_t *sock);

/**
 * @brief 通过套接字发送数据
 * @param sock 套接字指针
 * @param pkt 数据包指针
 * @return 发送结果：0成功，负值表示错误
 * 
 * @note 使用套接字绑定的端口作为源端口
 * @warning 目标地址必须在数据包中指定
 */
int cdn_sock_sendto(cdn_sock_t *sock, cdn_pkt_t *pkt);

/**
 * @brief 通过套接字接收数据
 * @param sock 套接字指针
 * @return 接收到的数据包指针，NULL表示无数据
 * 
 * @note 非阻塞接收，需要定期调用
 * @warning 使用后需要调用cdn_pkt_free()释放内存
 */
cdn_pkt_t *cdn_sock_recvfrom(cdn_sock_t *sock);

/** @} */ // 套接字管理API

/**
 * @defgroup cdnet_core_management 命名空间管理API
 * @brief 命名空间初始化和维护功能
 * @{
 */

/**
 * @brief 协议栈主循环处理
 * @param ns 命名空间指针
 * 
 * @details 处理接收、发送、路由等周期性任务
 * @note 必须在主循环中定期调用
 * @warning 不调用会导致数据丢失
 */
void cdn_routine(cdn_ns_t *ns);

/**
 * @brief 初始化命名空间
 * @param ns 命名空间指针
 * @param free_pkt 空闲数据包内存池链表头
 * @param free_frm 空闲帧内存池链表头
 * 
 * @details 初始化命名空间的所有数据结构和内存池
 * @note 在使用任何其他功能前必须先调用此函数
 */
void cdn_init_ns(cdn_ns_t *ns, list_head_t *free_pkt, list_head_t *free_frm);

/**
 * @brief 添加网络接口
 * @param ns 命名空间指针
 * @param dev 底层设备指针
 * @param net 网络号
 * @param mac MAC地址
 * @return 添加结果：0成功，负值表示错误
 * 
 * @note 将网络设备添加到命名空间
 * @warning 网络号和MAC地址必须在系统中唯一
 */
int cdn_add_intf(cdn_ns_t *ns, void *dev, uint8_t net, uint8_t mac);

/** @} */ // 命名空间管理API

/**
 * @defgroup cdnet_core_helpers 辅助函数
 * @brief 内存管理和数据包处理的辅助函数
 * @{
 */

/**
 * @brief 准备数据包发送
 * @param sock 套接字指针
 * @param pkt 数据包指针
 * 
 * @details 设置数据包的源端口和有效数据指针
 * @note 在发送前自动调用，无需手动调用
 * @warning 会修改数据包内部状态
 */
static inline void cdn_pkt_prepare(cdn_sock_t *sock, cdn_pkt_t *pkt)
{
    pkt->src.port = sock->port;
    if (pkt->dst.addr[0] == 0x10) { // localhost
        pkt->dat = pkt->frm->dat + 3;
    } else {
        cdn_route(sock->ns, pkt);
        pkt->dat = pkt->frm->dat + 3 + cdn_hdr_size_pkt(pkt);
    }
}

/**
 * @brief 分配数据包内存
 * @param ns 命名空间指针
 * @return 数据包指针，NULL表示内存不足
 * 
 * @details 从内存池中分配数据包和关联帧
 * @note 分配后需要初始化数据包内容
 * @warning 使用后必须调用cdn_pkt_free()释放
 */
static inline cdn_pkt_t *cdn_pkt_alloc(cdn_ns_t *ns)
{
    cd_frame_t *frame = (cd_frame_t *)list_get(ns->free_frm);
    if (!frame)
        return NULL;
    cdn_pkt_t *pkt = (cdn_pkt_t *)list_get(ns->free_pkt);
    if (!pkt) {
        list_put(ns->free_frm, &frame->node);
        return NULL;
    }
    memset(pkt, 0, sizeof(cdn_pkt_t));
    pkt->frm = frame;
    return pkt;
}

/**
 * @brief 释放数据包内存
 * @param ns 命名空间指针
 * @param pkt 数据包指针
 * 
 * @details 将数据包和帧归还到内存池
 * @note 必须在不再使用数据包后调用
 * @warning 释放后数据包指针变为无效
 */
static inline void cdn_pkt_free(cdn_ns_t *ns, cdn_pkt_t *pkt)
{
    if (pkt->frm) {
        list_put(ns->free_frm, &pkt->frm->node);
        pkt->frm = NULL;
    }
    list_put(ns->free_pkt, &pkt->node);
}

/** @} */ // 辅助函数

#endif // CDNET_CORE_H